#ifndef PERLIN_CGINC
#define PERLIN_CGINC

#include "Permutation.cginc"
#include "Gradient.cginc"

float2 fade2(float2 t)
{
	return t*t*t*(t*(t*6-15)+10);
}

float perlinNoise2(float2 xy)
{
	// Find unit square that contains point and find relative x,y of point in square
	float2 ij = floor(xy);
	xy -= ij;

	// Compute fade curves for each of x,y
	float2 uv = fade2(xy);

	// Hash coordinates at the 4 square corners
	float4 perm = perm2(ij);

	// Sample gradients at the 4 square corners (normalised to the range [-1, 1])
	float4 g0 = grad2(perm.xy);
	float4 g1 = grad2(perm.zw);

	// Compute gradient ramp at the 4 square corners
	float g00 = dot2(g0.xy, xy+float2( 0,  0));
	float g10 = dot2(g0.zw, xy+float2(-1,  0));
	float g01 = dot2(g1.xy, xy+float2( 0, -1));
	float g11 = dot2(g1.zw, xy+float2(-1, -1));

	// Blend the results along x
	float nx0 = lerp(g00, g10, uv.x);
	float nx1 = lerp(g01, g11, uv.x);

	// Blend the results along y
	float nxy = lerp(nx0, nx1, uv.y);

	// Return the result, roughly normalised to the range [-1, 1]
	return nxy;
}

float perlinNoise2(float2 xy, int octaves, float lacunarity, float gain)
{
	float sum = 0.0f;
	float freq = 1.0f;
	float amp = 1.0f;

	// Sum noise octaves
	for (int i=0; i<octaves; i++)
	{
		float n = perlinNoise2(xy*freq);
		sum += amp*n;
		freq *= lacunarity;
		amp *= gain;
	}

	// Return the result
	return sum;
}

float3 fade3(float3 t)
{
	return t*t*t*(t*(t*6-15)+10);
}

float gradDot3(float perm, float3 xyz)
{
    return dot3(permGrad2(perm), xyz);
}

float perlinNoise3(float3 xyz)
{
	// Find unit cube that contains point and find relative x,y,z of point in cube
	float3 ijk = floor(xyz);
	xyz -= ijk;

	// Compute fade curves for each of x,y,z
	float3 uvw = fade3(xyz);

	float k0 = ijk.z;
	float k1 = k0+1;

	// Hash coordinates at the 8 cube corners
	float4 perm = perm2(ijk.xy);
	float4 p0 = perm+k0;
	float4 p1 = perm+k1;

	// Get the gradient at the 8 cube corners
	float g000 = gradDot3(p0.r, xyz+float3( 0,  0,  0));
	float g100 = gradDot3(p0.g, xyz+float3(-1,  0,  0));
	float g010 = gradDot3(p0.b, xyz+float3( 0, -1,  0));
	float g110 = gradDot3(p0.a, xyz+float3(-1, -1,  0));
	float g001 = gradDot3(p1.r, xyz+float3( 0,  0, -1));
	float g101 = gradDot3(p1.g, xyz+float3(-1,  0, -1));
	float g011 = gradDot3(p1.b, xyz+float3( 0, -1, -1));
	float g111 = gradDot3(p1.a, xyz+float3(-1, -1, -1));

	// Blend the results along x
	float nx00 = lerp(g000, g100, uvw.x);
	float nx10 = lerp(g010, g110, uvw.x);
	float nx01 = lerp(g001, g101, uvw.x);
	float nx11 = lerp(g011, g111, uvw.x);

	// Blend the results along y
	float nxy0 = lerp(nx00, nx10, uvw.y);
	float nxy1 = lerp(nx01, nx11, uvw.y);

	// Blend the results along z
	float nxyz = lerp(nxy0, nxy1, uvw.z);

	// Return the result, roughly normalised to the range [-1, 1]
	return nxyz;
}

float perlinNoise3(float3 xyz, int octaves, float lacunarity, float gain)
{
	float sum = 0.0f;
	float freq = 1.0f;
	float amp = 1.0f;

	// Sum noise octaves
	for (int i=0; i<octaves; i++)
	{
		float n = perlinNoise3(xyz*freq);
		sum += amp*n;
		freq *= lacunarity;
		amp *= gain;
	}

	// Return the result
	return sum;
}

float perlinBillowedNoise2(float2 xy)
{
	return abs(perlinNoise2(xy));
}

float perlinBillowedNoise2(float2 xy, int octaves, float lacunarity, float gain)
{
	float sum = 0.0f;
	float freq = 1.0f;
	float amp = 1.0f;

	// Sum noise octaves
	for (int i=0; i<octaves; i++)
	{
		float n = perlinBillowedNoise2(xy*freq);
		sum += amp*n;
		freq *= lacunarity;
		amp *= gain;
	}

	// Return the result
	return sum;
}

float perlinRidgedNoise2(float2 xy)
{
	return 1.0-abs(perlinNoise2(xy));
}

float perlinRidgedNoise2(float2 xy, int octaves, float lacunarity, float gain)
{
	float sum = 0.0f;
	float freq = 1.0f;
	float amp = 1.0f;

	// Sum noise octaves
	for (int i=0; i<octaves; i++)
	{
		float n = perlinRidgedNoise2(xy*freq);
		sum += amp*n;
		freq *= lacunarity;
		amp *= gain;
	}

	// Return the result
	return sum;
}

#endif // PERLIN_CGINC